package com.lyb.hardware.io;

import com.lyb.constant.SystemConstant;
import com.lyb.hardware.AlpacaPCB;

import java.util.LinkedList;
import java.util.Queue;

/**
 * Direct Memory Access，直接存储器访问类
 * 通过该类来模拟系统直接使用主存进行I/O而不需要处理器的干预
 * 进程交由设备执行，执行结束后将进程放入就绪队列
 *
 * @author Mr.Alpaca
 * @version 1.4.0
 */
public class AlpacaDMA implements Runnable {
    /**
     * 可用设备实体
     */
    private final Queue<A> aQueue;
    private final Queue<B> bQueue;
    private final Queue<C> cQueue;

    public AlpacaDMA() {
        aQueue = new LinkedList<>();
        bQueue = new LinkedList<>();
        cQueue = new LinkedList<>();
        for (int i = 0; i < SystemConstant.DEVICE_A_NUM; i++) {
            aQueue.add(new A());
        }
        for (int i = 0; i < SystemConstant.DEVICE_B_NUM; i++) {
            bQueue.add(new B());
        }
        for (int i = 0; i < SystemConstant.DEVICE_C_NUM; i++) {
            cQueue.add(new C());
        }
    }

    /**
     * 多线程执行
     */
    private void go() {
        /* 若当前系统无阻塞进程则直接返回 */
        AlpacaPCB pcb = SystemConstant.MAIN_BOARD.getBlockQueue().poll();
        if (pcb == null) {
            return;
        }

        /* 获取系统阻塞进程的阻塞原因 */
        String param = pcb.getDescription();
        /* 获取运行设备 */
        char device = param.charAt(0);
        /* 获取运行时间 */
        int time = ((int) param.charAt(1) - 48) * 1000;

        if (device == 'A') {
            if (aQueue.isEmpty()) {
                SystemConstant.MAIN_BOARD.getBlockQueue().add(pcb);
            } else {
                A a = aQueue.poll();
                new Thread(() -> {
                    a.start();
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    a.finish();
                    /* 归还设备 */
                    aQueue.add(a);
                    /* 唤醒进程 */
                    SystemConstant.MAIN_BOARD.awake(pcb);
                }).start();
            }
        } else if (device == 'B') {
            if (bQueue.isEmpty()) {
                SystemConstant.MAIN_BOARD.getBlockQueue().add(pcb);
            } else {
                B b = bQueue.poll();
                new Thread(() -> {
                    b.start();
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    b.finish();
                    bQueue.add(b);
                    SystemConstant.MAIN_BOARD.awake(pcb);
                }).start();
            }
        } else if (device == 'C') {
            if (cQueue.isEmpty()) {
                SystemConstant.MAIN_BOARD.getBlockQueue().add(pcb);
            } else {
                C c = cQueue.poll();
                new Thread(() -> {
                    c.start();
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    c.finish();
                    cQueue.add(c);
                    SystemConstant.MAIN_BOARD.awake(pcb);
                }).start();
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            go();
        }
    }

    /* ********** Getter & Setter ********** */

    public Queue<A> getaQueue() {
        return aQueue;
    }

    public Queue<B> getbQueue() {
        return bQueue;
    }

    public Queue<C> getcQueue() {
        return cQueue;
    }
}
